1、功能介绍
XVC—Xilinx Virtual Cable,是Xilinx推出的基于TCP/IP协议的远程调试方法,可用于Xilinx FPGA的远程下载。其作用是可替代目前常用的Xilinx官方下载器Platform Cable USB Ⅱ,通过网线即可实现FPGA程序的加载和调试,简要结构如下图所示:
![](https://img-blog.csdnimg.cn/000a1c82dbec4da092f80023cef63a60.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARmVsdmVu,size_20,color_FFFFFF,t_70,g_se,x_16)
ZYNQ通过网线实现与调试机之间的数据传输,ARM处理器通过AXI 转JTAG IP核,将文件通过V7的JTAG口与V7进行交互,实现V7的程序下载与调试,简要结构如下图所示。
![](https://img-blog.csdnimg.cn/03b8d5fe6246436d8d8d23187bdc0162.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARmVsdmVu,size_20,color_FFFFFF,t_70,g_se,x_16)
2、硬件设计示意图
![](https://img-blog.csdnimg.cn/63e35bea9ed14e92bf44d4e5f52bb225.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARmVsdmVu,size_20,color_FFFFFF,t_70,g_se,x_16)
3、ZYNQ VIVADO功能设计
(1)、将axi转JTAG IP核添加在ZYNQ工程。具体操作:点击Tools,进入Project Settings ,进入IP,找到Repository Manager,将附件中的IP核添加到工程中;
![](https://img-blog.csdnimg.cn/cb61413536744d60b7133d1e046f7ff0.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARmVsdmVu,size_20,color_FFFFFF,t_70,g_se,x_16)
(2)、在BD文件中,添加AXI-Lite to JTAG 核,另增加一个AXI GPIO核,该4T245的使能信号拉到软件顶层,方便在顶层APP进行XVC功能选用。
![](https://img-blog.csdnimg.cn/6f645476dc1d4476b99850aef43d2921.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARmVsdmVu,size_13,color_FFFFFF,t_70,g_se,x_16) ![](https://img-blog.csdnimg.cn/7a6525fe137441638328a36b6d228c0d.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARmVsdmVu,size_12,color_FFFFFF,t_70,g_se,x_16)
(3)、由于ZYNQ工作时会有一个默认的高电平,V7的JTAG信号直接拉到ZYNQ时,ZYNQ会将V7的JTAG拉死,造成Xilinx仿真器使用时无法扫到FPGA,因而ZYNQ的JTAG输出信号需要进行相应处理。
![](https://img-blog.csdnimg.cn/9515aaf78d1e45b09b9b28042baf7791.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARmVsdmVu,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/c2bad267133a4c48bb40a28792577546.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARmVsdmVu,size_12,color_FFFFFF,t_70,g_se,x_16)
4、软件设计
修改内核uio驱动文件“uio_pdrv_genirq.c”:
![](https://img-blog.csdnimg.cn/f8c2731ef9ee4234b9488560ac2e2bd5.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARmVsdmVu,size_18,color_FFFFFF,t_70,g_se,x_16)
修改内核配置文件打开“CONFIG_OF”并且编译配置文件,再使用“make menuconfig”命令,进入内核配置菜单,关闭“cpu idle PM support”选项,使能“userspace I/O platform driver with generic IRQ handling”;修改设备树文件,更改uio驱动:
![](https://img-blog.csdnimg.cn/8152bced304a498f94391ca9a91bdc71.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARmVsdmVu,size_18,color_FFFFFF,t_70,g_se,x_16)
将xvcServer.c编译后生成的可执行文件传入板卡。注意:xvcServer.c中打开的是“/dev/uio0文件,如有冲突需修改设备树或者xvcServer.c文件。代码如下:
/* This work, "xvcServer.c", is a derivative of "xvcd.c" (https://github.com/tmbinc/xvcd)
* by tmbinc, used under CC0 1.0 Universal (http://creativecommons.org/publicdomain/zero/1.0/).
* "xvcServer.c" is licensed under CC0 1.0 Universal (http://creativecommons.org/publicdomain/zero/1.0/)
* by Avnet and is used by Xilinx for XAPP1251.
*
* Description : XAPP1251 Xilinx Virtual Cable Server for Linux
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAP_SIZE 0x10000
typedef struct {
uint32_t length_offset;
uint32_t tms_offset;
uint32_t tdi_offset;
uint32_t tdo_offset;
uint32_t ctrl_offset;
} jtag_t;
static int verbose = 0;
static int sread(int fd, void *target, int len) {
unsigned char *t = target;
while (len) {
int r = read(fd, t, len);
if (r sizeof(buffer)) {
fprintf(stderr, "buffer size exceeded\n");
return 1;
}
if (sread(fd, buffer, nr_bytes * 2) != 1) {
fprintf(stderr, "reading data failed\n");
return 1;
}
memset(result, 0, nr_bytes);
if (verbose) {
printf("\tNumber of Bits : %d\n", len);
printf("\tNumber of Bytes : %d \n", nr_bytes);
printf("\n");
}
int bytesLeft = nr_bytes;
int bitsLeft = len;
int byteIndex = 0;
int tdi, tms, tdo;
while (bytesLeft > 0) {
tms = 0;
tdi = 0;
tdo = 0;
if (bytesLeft >= 4) {
memcpy(&tms, &buffer[byteIndex], 4);
memcpy(&tdi, &buffer[byteIndex + nr_bytes], 4);
ptr->length_offset = 32;
ptr->tms_offset = tms;
ptr->tdi_offset = tdi;
ptr->ctrl_offset = 0x01;
/* Switch this to interrupt in next revision */
while (ptr->ctrl_offset)
{
}
tdo = ptr->tdo_offset;
memcpy(&result[byteIndex], &tdo, 4);
bytesLeft -= 4;
bitsLeft -= 32;
byteIndex += 4;
if (verbose) {
printf("LEN : 0x%08x\n", 32);
printf("TMS : 0x%08x\n", tms);
printf("TDI : 0x%08x\n", tdi);
printf("TDO : 0x%08x\n", tdo);
}
} else {
memcpy(&tms, &buffer[byteIndex], bytesLeft);
memcpy(&tdi, &buffer[byteIndex + nr_bytes], bytesLeft);
ptr->length_offset = bitsLeft;
ptr->tms_offset = tms;
ptr->tdi_offset = tdi;
ptr->ctrl_offset = 0x01;
/* Switch this to interrupt in next revision */
while (ptr->ctrl_offset)
{
}
tdo = ptr->tdo_offset;
memcpy(&result[byteIndex], &tdo, bytesLeft);
if (verbose) {
printf("LEN : 0x%08x\n", 32);
printf("TMS : 0x%08x\n", tms);
printf("TDI : 0x%08x\n", tdi);
printf("TDO : 0x%08x\n", tdo);
}
break;
}
}
if (write(fd, result, nr_bytes) != nr_bytes) {
perror("write");
return 1;
}
} while (1);
/* Note: Need to fix JTAG state updates, until then no exit is allowed */
return 0;
}
int main(int argc, char **argv) {
int i;
int s;
int c;
int fd_uio;
struct sockaddr_in address;
opterr = 0;
while ((c = getopt(argc, argv, "v")) != -1)
switch (c) {
case 'v':
verbose = 1;
break;
case '?':
fprintf(stderr, "usage: %s [-v]\n", *argv);
return 1;
}
fd_uio = open("/dev/uio0", O_RDWR );
if (fd_uio < 1) {
fprintf(stderr,"Failed to Open UIO Device\n");
return -1;
}
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0) {
perror("socket");
return 1;
}
volatile jtag_t* ptr = (volatile jtag_t*) mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
fd_uio, 0);
if (ptr == MAP_FAILED)
fprintf(stderr, "MMAP Failed\n");
close(fd_uio);
i = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof i);
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(2542);
address.sin_family = AF_INET;
if (bind(s, (struct sockaddr*) &address, sizeof(address)) < 0) {
perror("bind");
return 1;
}
if (listen(s, 5) < 0) {
perror("listen");
return 1;
}
fd_set conn;
int maxfd = 0;
FD_ZERO(&conn);
FD_SET(s, &conn);
maxfd = s;
while (1) {
fd_set read = conn, except = conn;
int fd;
if (select(maxfd + 1, &read, 0, &except, 0) < 0) {
perror("select");
break;
}
for (fd = 0; fd maxfd) {
maxfd = newfd;
}
FD_SET(newfd, &conn);
}
}
else if (handle_data(fd,ptr)) {
if (verbose)
printf("connection closed - fd %d\n", fd);
close(fd);
FD_CLR(fd, &conn);
}
}
else if (FD_ISSET(fd, &except)) {
if (verbose)
printf("connection aborted - fd %d\n", fd);
close(fd);
FD_CLR(fd, &conn);
if (fd == s)
break;
}
}
}
munmap((void *) ptr, MAP_SIZE);
return 0;
}
5、使用方法
如图所示准备环境:
![](https://img-blog.csdnimg.cn/d39cba21e00d4f238c33e67c1d576739.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARmVsdmVu,size_20,color_FFFFFF,t_70,g_se,x_16)
模块上电、系统启动完成后,设置模块及调试机网络地址(同网段,不同地址),后台运行xvcServer程序(ZYNQ APP)(./xvcServer.elf &);
如果模块设计兼容两种调试方式(JTAG/XVC),需要使能XVC,一般为GPIO拉高或者拉低;
在调试机打开vivado2018.3软件(验证时低版本可能会有问题),点击“Open Hardware Manager”;
在tcl console 命令窗口中输入 “connect_hw_server”,或者点击open target->auto connect;
![](https://img-blog.csdnimg.cn/2525cfbd174a4d75b02474535f2e7862.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARmVsdmVu,size_20,color_FFFFFF,t_70,g_se,x_16)
右键localhost,选择Add Xilinx Virtual Cable(xvc),填写板子上的ZYNQ IP,即可。
![](https://img-blog.csdnimg.cn/b87b03eb73b24267846afed72104d865.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARmVsdmVu,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/93935f0922464cb4a087ef104c180807.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARmVsdmVu,size_20,color_FFFFFF,t_70,g_se,x_16)
可以看到通过将ZYNQ作为JTAG使用,通过网络就连上了模块上面的V7。
6、不足
XVC的性能受处理器影响,在处理器的资源使用过多时,可能会影响XVC的性能。
更多参考xilinx官方材料:
xapp1251.zip
xapp1251-xvc-zynq-petalinux.pdf
|